/*
 * @Author: HanYaJun
 * @Date: 2023-01-11 09:39:10
 * @Email: hanyajun@wedobest.com.cn
 * @Description: 星消灭界面
 */

import dwfUIConfig from '../../../dwf/scripts/Global/dwfUIConfig';
import { guiForm } from "../../../Public/game/guiManager/UIFrame/BaseForm";
import { GTR } from '../../../Public/GTR';
import xxmUIConfig from "../Global/xxmUIConfig";
import { xxmPrefabPathContainer, xxmPrefabEnum, IxxmAssetCfg } from '../Global/xxmUIConfig';
import { AsyncQueue } from '../../../Public/game/utils/AsyncQueue';
import xxmGameEvents from '../Global/xxmGameEvents';
import LocalData from '../../../Public/game/gameData/LocalData';
import { xxmItemBlock, imageTypeEumn, BlockColor } from './xxmItemBlock';
import { LocalCacheDataEnum } from '../Global/xxmDefaultPlayerData';
import GameUtil from '../../../dwf/scripts/Manager/GameUtil';
import { xxmChangeBlock } from './xxmChangeBlock';

/**
 * @description: 方块的基本配置
 */
export class GameConfig {
    /**行数 */
    public static row: number = 10;
    /**列数 */
    public static column: number = 10;
    /**方块的尺寸 */
    public static size: number = 72;
    /**间隔 */
    public static spacing: number = 0;
    /**边距 */
    public static padding: number = 0;

}

/**
 * @description: 遍历方向
 */
export enum TraverseDirectionEnum {
    /**无方向 */
    NONE = 0,
    /**上 */
    UP = 1,
    /**下 */
    DOWN = 2,
    /**左 */
    LEFT = 3,
    /**右 */
    RIGHT = 4
}

/**
 * @description: 摆放方块的位置信息
 */
export interface IBlockPosInfo {
    /**是否显示 */
    isBlock: boolean;
    /**颜色 */
    color: number;
}

let trace = function (...args) {
    GTR.logg.log("xxmPlayUI【HYJ】", ...args);
}
let traceError = function (...args) {
    GTR.logg.error("xxmPlayUI【HYJ】", ...args);
}

const { ccclass, property } = cc._decorator;
@ccclass
export default class xxmPlayUI extends cc.Component {
    @property({
        type: cc.Node,
        displayName: "方块预制",
    })
    private itemPrefab: cc.Node;

    @property({
        type: cc.Node,
        displayName: '改变方块颜色值'
    })
    private blockPrefab: cc.Node;

    // #region (地图数据)
    /**方块坐标 */
    private positionMap: cc.Vec3[][] = [];
    /**地图宽度 */
    private width: number = null;
    /**地图高度 */
    private height: number = null;
    /**起始 x 坐标 */
    private beginX: number = null;
    /**起始 y 坐标 */
    private beginY: number = null;
    // #endregion

    /**相同的方块*/
    private sameBlock: cc.Node[] = [];
    /**方块放置背景 */
    private blockBg: cc.Node = null;
    /**延迟时间设置 */
    private delayTimes: number = 0.3;
    /**消除分数 */
    private scoreLabel: cc.Label = null;
    /**最高分 */
    private highestScore: cc.Label = null;
    /**摆放坐标 */
    private placeCoordinate: IBlockPosInfo[][] = [];
    /**保存可放置的位置 */
    private canPlacePos: number[][] = [];
    /**首次放置时周围颜色值 */
    private colorValue: number[] = [];
    /**销毁该窗体 */
    willDestory = true;
    /**单个摆放时颜色值设定 */
    private oneBlockCol: number[] = [];
    /**检查剩余方块是否有同色的 */
    private residueSameBlock: cc.Node[] = [];
    /**改变颜色 */
    private usePropBg: cc.Node = null;
    /**点击方块坐标 */
    private clickBlock: number[] = [];
    /**点击方块颜色值 */
    private imageType: number = null;
    /**改变颜色 */
    private changeCloor: boolean = false;
    /**改变方块颜色 */
    private blockLayout: cc.Node = null;
    /**范围消除 */
    private rangeRemoved: boolean = true;
    /**等级 */
    private levelProgress: cc.Node = null;
    /**目标分 */
    private targetScore: cc.Node = null;
    /**消除得分 */
    private lumpAndScore: cc.Node = null;

    /**
     * @description: 则在所有组件的 update 之前调用。通常用于需要在所有组件的 onLoad 初始化完毕后执行的逻辑
     */
    protected start() {
        let buttons: cc.Button[] = this.node.getComponentsInChildren(cc.Button);
        buttons.forEach((button) => {
            button.node.on('click', this.btnClickCallBack, this);
        });
    }

    /**
     * @description: onLoad 总是会在任何 start 方法调用前执行
     */
    protected onLoad(): void {
        // 加载关卡目标分数页
        GTR.ui.open(xxmUIConfig.LevelScoreUI);
        // 方块背景
        this.blockBg = cc.find('spBg/blockBg', this.node);
        this.usePropBg = cc.find('spBg/usePropBg', this.node);
        this.blockLayout = cc.find('spBg/usePropBg/block/blockLayout', this.node);
        this.levelProgress = cc.find('spBg/top/levelSprite/levelProgress', this.node);
        this.targetScore = cc.find('spBg/top/targetLayout/targetScore', this.node);
        this.lumpAndScore = cc.find('spBg/top/lumpAndScore', this.node);
        this.registeredEvents();
        this.initData();
    }

    /**
     * @description: 注册事件
     */
    private registeredEvents(): void {
        GTR.event.registerEvent(xxmGameEvents.CLICK_BLOCK, this.dealClearUp, this);
        GTR.event.registerEvent(xxmGameEvents.SHOWlEVEL, this.generateLevel, this);
        GTR.event.registerEvent(xxmGameEvents.REPLAY, this.initData, this);
        GTR.event.registerEvent(xxmGameEvents.CHANGE_COLOR, this.changeBolckCol, this);
        GTR.event.registerEvent(xxmGameEvents.CHANGE_CLICK_COLOR, this.changeClickBlock, this);
    }

    /**
     * @description: 销毁事件
     */
    private releaseEvents(): void {
        GTR.event.releaseEvent(this, xxmGameEvents.CLICK_BLOCK);
        GTR.event.releaseEvent(this, xxmGameEvents.SHOWlEVEL);
        GTR.event.releaseEvent(this, xxmGameEvents.REPLAY);
        GTR.event.releaseEvent(this, xxmGameEvents.CHANGE_COLOR);
        GTR.event.releaseEvent(this, xxmGameEvents.CHANGE_CLICK_COLOR);
    }

    /**
     * @description: 初始化数据
     */
    private initData(): void {
        this.blockBg.removeAllChildren();
        this.blockLayout.removeAllChildren();
        this.positionMap = [];
        this.sameBlock = [];
        this.placeCoordinate = [];
        this.canPlacePos = [];
        this.colorValue = [];
        this.oneBlockCol = [];
        this.residueSameBlock = [];
        this.clickBlock = [];
        this.imageType = null;
        this.changeCloor = false;
        this.rangeRemoved = true;
        this.lumpAndScore.active = false;
        // 设置方块背景缩放
        this.blockBg.scale = cc.v3(0, 0);
        // 生成地图
        this.createMatrix();
        // 摆放方块位图提前生成
        this.placementCoordinate();
        // 提前创建关卡
        this.createLevelPro();
        // 展示分数
        this.displayBlock();
        // 改变颜色
        this.usePropBg.active = false;
        // 获取等级
        let levelPro: number = null;
        let getLevel = LocalData.getItem(LocalCacheDataEnum.LEVEL);
        if (getLevel) {
            levelPro = getLevel;
        } else {
            levelPro = 1;
            LocalData.setItem(LocalCacheDataEnum.LEVEL, 1);
        }
        if (this.levelProgress) {
            let level: cc.Label = this.levelProgress.getComponent(cc.Label);
            level.string = levelPro + '';
        }
        // 目标分
        if (this.targetScore) {
            let score: cc.Label = this.targetScore.getComponent(cc.Label);
            let getScore = LocalData.getItem(LocalCacheDataEnum.TARGETSCORE);
            if (getScore) {
                score.string = String(getScore);
            } else {
                score.string = String(1000);
            }
        }
    }

    /**
     * @description: 展示分数
     * @return {*}
     */
    private displayBlock(): void {
        // 分数
        let scoreNode: cc.Node = cc.find('spBg/top/score', this.node);
        if (scoreNode) {
            this.scoreLabel = scoreNode.getComponent(cc.Label);
            let getScore = LocalData.getItem(LocalCacheDataEnum.SCORE);
            if (getScore) {
                this.scoreLabel.string = String(getScore);
            } else {
                this.scoreLabel.string = String(0);
            }
        }
        // 最高分
        let highestNode: cc.Node = cc.find('spBg/top/scoreLayout/highestScore', this.node);
        if (highestNode) {
            this.highestScore = highestNode.getComponent(cc.Label);
            let getHeight = LocalData.getItem(LocalCacheDataEnum.HIGHESTSCORE);
            if (getHeight) {
                this.highestScore.string = String(getHeight);
            } else {
                this.highestScore.string = String(0);
            }
        }
    }

    /**
     * @description: 生成关卡
     */
    private generateLevel(): void {
        for (let r = 0; r < GameConfig.row; r++) {
            for (let c = 0; c < GameConfig.column; c++) {
                // 方块预制
                let itemNode: cc.Node = cc.instantiate(this.itemPrefab);
                itemNode.parent = this.blockBg;
                // 设置方块位置
                const item: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
                let complete: IBlockPosInfo = this.placeCoordinate[r][c];
                if (complete) {
                    // 判断是否上锁
                    let isBlock: boolean = complete.isBlock;
                    let imageType: number = complete.color;
                    if (!isBlock && imageType) {
                        // trace('图片颜色值为, imageType:', imageType);
                        item.initData({ position: this.positionMap[r][c], colorValue: imageType });
                        item.setDimensions([r, c]);
                    }
                }
            }
        }
        // 设置方块背景缩放
        cc.tween(this.blockBg)
            .to(0.2, { scale: cc.v3(1, 1, 1) })
            .start();
    }

    /**
     * @description: 生成摆放位图
     */
    private placementCoordinate(): void {
        for (let i = 0; i < GameConfig.row; i++) {
            let pos = [];
            for (let j = 0; j < GameConfig.column; j++) {
                let info: IBlockPosInfo = {
                    isBlock: true,
                    color: 0
                }
                pos.push(info);
            }
            this.placeCoordinate.push(pos);
        }
    }

    /**
     * @description: 创建关卡
     */
    private createLevelPro(): void {
        // 获取等级
        let levelProgress: number = null;
        let getLevel = LocalData.getItem(LocalCacheDataEnum.LEVEL);
        if (getLevel) {
            levelProgress = getLevel;
        } else {
            levelProgress = 1;
            LocalData.setItem(LocalCacheDataEnum.LEVEL, 1);
        }
        if (1 <= levelProgress && levelProgress <= 3) {
            let arr = [
                { logarithmNum: [1, 1], joinNum: [11, 20] },
                { logarithmNum: [3, 4], joinNum: [6, 10] },
                { logarithmNum: [3, 5], joinNum: [4, 5] },
                { logarithmNum: [3, 6], joinNum: [2, 3] }
            ];
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();
        } else if (4 <= levelProgress && levelProgress <= 8) {
            let arr = [
                { logarithmNum: [3, 5], joinNum: [6, 10] },
                { logarithmNum: [2, 6], joinNum: [4, 5] },
                { logarithmNum: [4, 8], joinNum: [2, 3] }
            ];
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();

        } else if (9 <= levelProgress && levelProgress <= 13) {
            let arr = [];
            if (levelProgress === 10) {
                arr.push(
                    { logarithmNum: [1, 1], joinNum: [11, 20] },
                    { logarithmNum: [2, 7], joinNum: [4, 5] },
                    { logarithmNum: [4, 10], joinNum: [2, 3] }
                );
            } else {
                arr.push(
                    { logarithmNum: [2, 4], joinNum: [6, 10] },
                    { logarithmNum: [2, 7], joinNum: [4, 5] },
                    { logarithmNum: [4, 10], joinNum: [2, 3] }
                );
            }
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();

        } else if (14 <= levelProgress && levelProgress <= 17) {
            let arr = [];
            if (levelProgress === 15) {
                arr.push(
                    { logarithmNum: [1, 1], joinNum: [11, 20] },
                    { logarithmNum: [4, 7], joinNum: [4, 5] },
                    { logarithmNum: [5, 10], joinNum: [2, 3] }
                );
            } else {
                arr.push(
                    { logarithmNum: [1, 4], joinNum: [6, 10] },
                    { logarithmNum: [4, 7], joinNum: [4, 5] },
                    { logarithmNum: [5, 10], joinNum: [2, 3] }
                );
            }
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();

        } else if (18 <= levelProgress && levelProgress <= 21) {
            let arr = [];
            if (levelProgress === 20) {
                arr.push(
                    { logarithmNum: [1, 1], joinNum: [11, 20] },
                    { logarithmNum: [4, 7], joinNum: [4, 5] },
                    { logarithmNum: [6, 13], joinNum: [2, 3] }
                );
            } else {
                arr.push(
                    { logarithmNum: [1, 3], joinNum: [6, 10] },
                    { logarithmNum: [4, 7], joinNum: [4, 5] },
                    { logarithmNum: [6, 13], joinNum: [2, 3] }
                );
            }
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();

        } else if (22 <= levelProgress && levelProgress <= 24) {
            let arr = [
                { logarithmNum: [1, 2], joinNum: [6, 10] },
                { logarithmNum: [5, 8], joinNum: [4, 5] },
                { logarithmNum: [8, 15], joinNum: [2, 3] }
            ];
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();
        } else {
            let remainder: number = levelProgress % 5;
            let arr = [];
            if (remainder) {
                // 不是 5 的倍数
                arr.push(
                    { logarithmNum: [1, 2], joinNum: [6, 10] },
                    { logarithmNum: [5, 8], joinNum: [4, 5] },
                    { logarithmNum: [10, 15], joinNum: [2, 3] }
                );
            } else {
                // 是 5 的倍数
                arr.push(
                    { logarithmNum: [1, 1], joinNum: [11, 20] },
                    { logarithmNum: [5, 8], joinNum: [4, 5] },
                    { logarithmNum: [10, 15], joinNum: [2, 3] }
                );
            }
            for (let i = 0; i < arr.length; i++) {
                let arrData = arr[i];
                let logar: number[] = arrData.logarithmNum;
                let join: number[] = arrData.joinNum;
                let joinNum: number = GameUtil.randomIntBound(join[0], join[1]);
                let logarithm: number = GameUtil.randomIntBound(logar[0], logar[1]);
                this.logarAndJoin({ logarithmNum: logarithm, joinNum: joinNum });
            }
            // 摆放位图上剩余格子
            this.blockBgResidueCount();
        }
    }

    /**
     * @description: 摆放位图剩余格子
     */
    private blockBgResidueCount() {
        // 生成单个数量
        let surplus: number = 0;
        // 生成多个数量
        let manyCount: number = 0;
        for (let r = 0; r < GameConfig.row; r++) {
            for (let c = 0; c < GameConfig.column; c++) {
                // 设置方块位置
                let complete: IBlockPosInfo = this.placeCoordinate[r][c];
                if (complete) {
                    // 判断是否上锁
                    let isBlock: boolean = complete.isBlock;
                    if (isBlock) {
                        surplus++;
                        // 未上锁生成单个的
                        this.oneBlockCol = [];
                        this.oneRoundColor({ rowV: r, colV: c });
                        let blockCol: number = this.oneColorValue();
                        trace('单个生成时的颜色值设置, blockCol:', blockCol);
                        this.placeCoordinate[r][c].isBlock = false;
                        this.placeCoordinate[r][c].color = blockCol;
                    } else {
                        manyCount++;
                    }
                }
            }
        }
        trace('生成多个的总数, manyCount:', manyCount, '单个生成的总数, surplus:', surplus);
    }

    /**
     * @description: 生成单个周围颜色值判断
     * @param {object} data
     * @return {*}
     */
    private oneRoundColor(data: { rowV: number, colV: number }) {
        // 判断是否可以放置
        for (let i = data.rowV - 1; i <= data.rowV + 1; i++) {
            for (let j = data.colV - 1; j <= data.colV + 1; j++) {
                // 忽略越界
                if (i < 0 || i > GameConfig.row - 1 || j < 0 || j > GameConfig.column - 1) {
                    continue;
                }
                // 忽略斜角
                if (i != data.rowV && j != data.colV) {
                    continue;
                }
                // 忽略自己
                if (i == data.rowV && j == data.colV) {
                    continue;
                }
                // 取到相邻节点(正相邻)
                let complete: IBlockPosInfo = this.placeCoordinate[i][j];
                let colValue: number = complete.color;
                this.oneBlockCol.push(colValue);
            }
        }
    }

    /**
     * @description: 返回生成单个颜色值
     */
    private oneColorValue(): number {
        trace('返回生成单个颜色值, this.oneBlockCol:', this.oneBlockCol);
        if (this.oneBlockCol.length) {
            // 先排序后去重
            this.oneBlockCol.sort();
            for (let i = 0; i < this.oneBlockCol.length; i++) {
                // 两两比较删除后面的
                if (this.oneBlockCol[i] === this.oneBlockCol[i + 1]) {
                    this.oneBlockCol.splice(i, 1);
                    i--;
                }
            }
            let colorValue: number[] = [1, 2, 3, 4, 5];
            for (let m = 0; m < colorValue.length; m++) {
                for (let n = 0; n < this.oneBlockCol.length; n++) {
                    if (colorValue[m] === this.oneBlockCol[n]) {
                        colorValue.splice(m, 1);
                    }
                }
            }
            // 取颜色数组中的随机值
            let randomVal: number = GameUtil.randomIntBound(0, colorValue.length - 1);
            let imagType: number = colorValue[randomVal];
            return imagType;
        }
    }

    /**
     * @description: 每次生成方块数量
     * @param {object} data logarithmNum: 生成对数, joinNum: 相连数
     * @return {*}
     */
    private logarAndJoin(data: { logarithmNum: number, joinNum: number }): void {
        trace('生成对数, jionNumber:', data.logarithmNum);
        for (let i = 0; i < data.logarithmNum; i++) {
            // 先清空方块存放位置
            this.canPlacePos = [];
            // 首位置颜色值置空
            this.colorValue = [];
            this.sameColorLogarithm(data.joinNum);
        }
    }

    /**
     * @description: 生成同色相连方块
     * @param {number} jionNumber 相连个数
     * @return {*}
     */
    private sameColorLogarithm(jionNumber: number) {
        trace('生成同色相连个数, jionNumber:', jionNumber);
        // 首次位置 行[0,9] 和 列[0,9]
        let firstRow: number = GameUtil.randomIntBound(0, 9);
        let firstCol: number = GameUtil.randomIntBound(0, 9);
        this.canPlacePosition({
            joineNum: jionNumber,
            nextRow: firstRow,
            nextCol: firstCol,
            fixedValue: jionNumber
        });
        let blockCol: number = this.setBlockColor();
        trace('可存放位置, this.canPlacePos:', this.canPlacePos, '颜色值设置, blockCol:', blockCol);
        // 设置锁状态和颜色值
        for (let i = 0; i < this.canPlacePos.length; i++) {
            let posValue: number[] = this.canPlacePos[i];
            this.placeCoordinate[posValue[0]][posValue[1]].isBlock = false;
            this.placeCoordinate[posValue[0]][posValue[1]].color = blockCol;
        }
    }

    /**
     * @description: 判断放置位置
     * @param {cc} data joineNum: 相连数, nextRow: 行值, nextCol: 列值, fixedValue: 固定值用户判断首位置周围颜色
     * @return {*}
     */
    private canPlacePosition(data: { joineNum: number, nextRow: number; nextCol: number, fixedValue: number }): void {
        // 保存可以放置的位置
        let canPlace: number[][] = [];
        let firstRow: number = data.nextRow;
        let firstCol: number = data.nextCol;
        // 判断是否继续循环
        if (data.joineNum === 0) {
            return;
        }
        // 判断是否可以放置
        for (let i = firstRow - 1; i <= firstRow + 1; i++) {
            for (let j = firstCol - 1; j <= firstCol + 1; j++) {
                // 忽略越界
                if (i < 0 || i > GameConfig.row - 1 || j < 0 || j > GameConfig.column - 1) {
                    continue;
                }
                // 忽略斜角
                if (i != firstRow && j != firstCol) {
                    continue;
                }
                // 自己
                if (i == firstRow && j == firstCol) {
                    let complete: IBlockPosInfo = this.placeCoordinate[i][j];
                    // 判断是否上锁
                    let isBlock: boolean = complete.isBlock;
                    if (isBlock) {
                        // 未上锁,设置锁
                        this.placeCoordinate[i][j].isBlock = false;
                        let pos: number[] = [];
                        pos.push(i, j);
                        // 保存可存放位置
                        this.canPlacePos.push(pos);
                        continue;
                    } else {
                        this.canPlacePosition({
                            joineNum: data.joineNum,
                            nextRow: GameUtil.randomIntBound(0, 9),
                            nextCol: GameUtil.randomIntBound(0, 9),
                            fixedValue: data.fixedValue
                        });
                        return;
                    }
                }
                // 取到相邻节点(正相邻)
                let complete: IBlockPosInfo = this.placeCoordinate[i][j];
                // 判断是否上锁
                let isBlock: boolean = complete.isBlock;
                if (isBlock) {
                    // 未上锁时,保存可放置位置
                    let pos: number[] = [];
                    pos.push(i, j);
                    canPlace.push(pos);
                } else {
                    // 首位置颜色值置空
                    this.colorValue = [];
                    // 上锁时,保存颜色值
                    if (data.joineNum === data.fixedValue) {
                        let colValue: number = complete.color;
                        this.colorValue.push(colValue);
                    }
                }
            }
        }
        if (canPlace.length) {
            // 可放置时设置下一次随机位置
            if (canPlace.length === 1) {
                // 只有一个位置可以放
                let rowV: number = canPlace[0][0];
                let colV: number = canPlace[0][1];
                let loopNum: number = data.joineNum - 1;
                this.canPlacePosition({
                    joineNum: loopNum,
                    nextRow: rowV,
                    nextCol: colV,
                    fixedValue: data.fixedValue
                });
            } else {
                // 可放置多个位置
                let nextDot: number = GameUtil.randomIntBound(0, canPlace.length - 1);
                let rowV: number = canPlace[nextDot][0];
                let colV: number = canPlace[nextDot][1];
                let loopNum: number = data.joineNum - 1;
                this.canPlacePosition({
                    joineNum: loopNum,
                    nextRow: rowV,
                    nextCol: colV,
                    fixedValue: data.fixedValue
                });
            }
        }
    }

    /**
     * @description: 设置方块颜色值
     */
    private setBlockColor(): number {
        trace('设置方块颜色值, this.colorValue:', this.colorValue);
        if (this.colorValue.length) {
            // 周围有放置
            if (this.colorValue.length === 1) {
                let colValue: number[] = [1, 2, 3, 4, 5];
                for (let k = 1; k < 6; k++) {
                    if (k === this.colorValue[0]) {
                        colValue.splice(k - 1, 1);
                    }
                }
                // 取颜色数组中的随机值
                let randomValue: number = GameUtil.randomIntBound(0, colValue.length - 1);
                let imagType: number = colValue[randomValue];
                return imagType;
            } else {
                // 先排序后去重
                this.colorValue.sort();
                for (let j = 0; j < this.colorValue.length; j++) {
                    // 两两比较删除后面的
                    if (this.colorValue[j] === this.colorValue[j + 1]) {
                        this.colorValue.splice(j, 1);
                        j--;
                    }
                }
                let colorValue: number[] = [1, 2, 3, 4, 5];
                for (let m = 0; m < colorValue.length; m++) {
                    for (let n = 0; n < this.colorValue.length; n++) {
                        if (colorValue[m] === this.colorValue[n]) {
                            colorValue.splice(m, 1);
                        }
                    }
                }
                // 取颜色数组中的随机值
                let randomVal: number = GameUtil.randomIntBound(0, colorValue.length - 1);
                let imagType: number = colorValue[randomVal];
                return imagType;
            }
        } else {
            // 周围都未放置
            let imagType: number = GameUtil.randomIntBound(1, 5);
            return imagType;
        }
    }

    /**
     * @description: 加载方块预制
     * @param {function} finish
     */
    private loadPrefab(finish: () => void): void {
        // 方块预制地址
        let cfg: IxxmAssetCfg = xxmPrefabPathContainer[xxmPrefabEnum.ItemBlock];
        if (cfg) {
            GTR.res.load(cfg.path, cc.Prefab, (error, prefab) => {
                if (error) {
                    traceError('方块预制加载失败');
                    return;
                } else {
                    this.itemPrefab = prefab;
                    finish();
                }
            });
        }
    }

    /**
     * @description: 创建一个 10 X 10 矩阵 生成位置表
     */
    private createMatrix(): void {
        // 先清除后设置
        this.positionMap = [];
        // 计算宽高
        this.width = (GameConfig.padding * 2) + (GameConfig.size * GameConfig.column) + (GameConfig.spacing * (GameConfig.column - 1));
        this.height = (GameConfig.padding * 2) + (GameConfig.size * GameConfig.row) + (GameConfig.spacing * (GameConfig.row - 1));
        // 以左下角为原点,计算第一个方块的位置
        this.beginX = -(this.width / 2) + GameConfig.padding + (GameConfig.size / 2);
        this.beginY = -(this.height / 2) + GameConfig.padding + (GameConfig.size / 2);
        // 计算所有方块的位置
        // 从左到右计算每一列方块的位置
        for (let c = 0; c < GameConfig.row; c++) {
            let columnSet = [];
            let y: number = this.beginY + c * (GameConfig.size + GameConfig.spacing);
            // 从下到上计算该列的每一个方块的位置
            for (let r = 0; r < GameConfig.column; r++) {
                let x: number = this.beginX + r * (GameConfig.size + GameConfig.spacing);
                columnSet.push(cc.v2(x, y));
            }
            this.positionMap.push(columnSet);
        }
    }

    /**
     * @description: 创建方块
     */
    // private createBlock(): void {
    //     for (let r = 0; r < GameConfig.row; r++) {
    //         let itemBlockScript = [];
    //         for (let c = 0; c < GameConfig.column; c++) {
    //             let itemNode: cc.Node = cc.instantiate(this.itemPrefab);
    //             itemNode.parent = this.blockBg;
    //             // 设置方块位置
    //             const item: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
    //             item.initData({ position: this.positionMap[r][c] });
    //             itemBlockScript.push(item);
    //             item.setDimensions([r, c]);
    //         }
    //         this.allitemBlockScript.push(itemBlockScript);
    //     }
    //     // 设置方块背景缩放
    //     cc.tween(this.blockBg)
    //         .to(0.2, { scale: cc.v3(1, 1, 1) })
    //         .start();
    // }

    /**
     * @description: 处理消除
     * @param {object} data 二维坐标点
     * @return {*}
     */
    private dealClearUp(data?: { pot }): void {
        if (this.changeCloor) {
            // 道具:改变方块颜色
            return;
        }
        if (!this.rangeRemoved) {
            // 道具: 范围消除
            return;
        }
        if (data) {
            // 先清除相同方块
            this.sameBlock = [];
            this.checkClearUp(data.pot[0], data.pot[1]);
        }
        trace("相同方块,sameBlock:", this.sameBlock);
        let asyncQueue: AsyncQueue = new AsyncQueue();
        asyncQueue.enable = true;
        asyncQueue.push((next) => {
            this.carrayRemove(next);
            trace('执行消除');
        });
        asyncQueue.push((next) => {
            // 计算得分
            this.scoreCount(this.sameBlock.length);
            // 计算消除
            if (this.sameBlock.length) {
                this.currentRemove();
            }
            // 计算下落
            trace('计算下落');
            this.checkDrop(next);
        });
        asyncQueue.push((next) => {
            // 计算左移
            trace('计算左移');
            this.checkLeftMove(next);
        });
        // 检测是否还有可消除的
        asyncQueue.push((next) => {
            // 没有可消除,结束游戏
            trace('结束游戏');
            this.checkGameEnd(next);
        });
        asyncQueue.push((next) => {
            trace('继续游戏');
            this.usePropBg.active = false;
            this.rangeRemoved = true;
            next();
        });
        asyncQueue.play();
    }

    /**
     * @description: 检测游戏结束
     * @param {Function} next
     */
    private checkGameEnd(next: Function): void {
        this.residueSameBlock = [];
        // 检测位图上剩余方块是否可消除
        for (let i = 0; i < this.blockBg.children.length; i++) {
            let itemNode: cc.Node = this.blockBg.children[i];
            let itemScript: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
            let pot: number[] = itemScript.getDimensions();
            let colorVal: imageTypeEumn = itemScript.getImageType();
            this.checkRoundSame({ row: pot[0], col: pot[1], colorValue: colorVal });
        }
        if (this.residueSameBlock.length) {
            // 有相同方块
        } else {
            // 没有相同方块, 全部消除进入下一关
            for (let i = 0; i < this.blockBg.children.length; i++) {
                let itemNode: cc.Node = this.blockBg.children[i];
                let pot: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
                pot.playClearAni(0.1);
            }
            this.scheduleOnce(() => {
                // 提前计算下一关数据
                this.entryNextPass();
                // 游戏结束播放下一关动画, 加载关卡目标分数页
                GTR.ui.open(xxmUIConfig.LevelScoreUI);
            }, 0.2);
        }
        this.scheduleOnce(() => {
            next && next();
        }, 0.2);
    }

    /**
     * @description: 进入下一关
     * @return {*}
     */
    private entryNextPass(): void {
        let getLevel = LocalData.getItem(LocalCacheDataEnum.LEVEL);
        if (getLevel) {
            LocalData.setItem(LocalCacheDataEnum.LEVEL, getLevel + 1);
        }
        this.initData();
    }

    /**
     * @description: 检测剩余方块是否有同色
     * @param {object} data
     */
    private checkRoundSame(data: { row: number, col: number, colorValue: number }): void {
        for (let i = data.row - 1; i <= data.row + 1; i++) {
            for (let j = data.col - 1; j <= data.col + 1; j++) {
                // 忽略越界
                if (i < 0 || i > GameConfig.row - 1 || j < 0 || j > GameConfig.column - 1) {
                    continue;
                }
                // 忽略自己
                if (i == data.row && j == data.col) {
                    continue;
                }
                // 忽略斜角
                if (i != data.row && j != data.col) {
                    continue;
                }
                // 取到相邻节点(正相邻)
                let blockItem: cc.Node = this.blockBg.getChildByName(i + "_" + j);
                if (!blockItem) {
                    continue;
                }
                let comp: xxmItemBlock = blockItem.getComponent(xxmItemBlock);
                // 已锁定状态 || 颜色不同
                if (comp.getBlockLock() || (data.colorValue != comp.getImageType())) {
                    continue;
                } else {
                    this.residueSameBlock.push(blockItem);
                }
            }
        }
    }

    /**
     * @description: 计算得分
     * @param {number} score 消除数量
     */
    private scoreCount(score: number): void {
        // 分数
        let scoreCou: number = score * score * 5;
        let getScore = LocalData.getItem(LocalCacheDataEnum.SCORE);
        if (getScore) {
            this.scoreLabel.string = String(scoreCou + getScore);
        } else {
            this.scoreLabel.string = String(scoreCou);
        }
        LocalData.setItem(LocalCacheDataEnum.SCORE, scoreCou + getScore);
        // 当前分数
        let currentScore = LocalData.getItem(LocalCacheDataEnum.SCORE);
        // 最高分
        let heightScore = LocalData.getItem(LocalCacheDataEnum.HIGHESTSCORE);
        if (heightScore) {
            if (heightScore > currentScore) {
                this.highestScore.string = String(heightScore);
            } else {
                this.highestScore.string = String(currentScore);
                LocalData.setItem(LocalCacheDataEnum.HIGHESTSCORE, currentScore);
            }
        } else {
            this.highestScore.string = String(currentScore);
            LocalData.setItem(LocalCacheDataEnum.HIGHESTSCORE, currentScore);
        }
    }

    /**
     * @description: 当前正在消除的方块颜色和获得分数
     * @return {*}
     */
    private currentRemove(): void {
        this.lumpAndScore.active = true;
        // 方块数
        let blockCount: cc.Node = this.lumpAndScore.getChildByName('lump');
        // 分数
        let removeScore: cc.Node = this.lumpAndScore.getChildByName('scoreNum');
        if (blockCount && removeScore) {
            let lump: cc.Label = blockCount.getComponent(cc.Label);
            let scoreNum: cc.Label = removeScore.getComponent(cc.Label);
            let score: number = this.sameBlock.length;
            scoreNum.string = String(score * score * 5);
            lump.string = String(score);
            let itemBlock: cc.Node = this.sameBlock[0];
            if (itemBlock) {
                let colorValue: imageTypeEumn = itemBlock.getComponent(xxmItemBlock).getImageType();
                switch (colorValue) {
                    case 1: {
                        let colorVal: string = BlockColor.blueVal;
                        lump.color = new cc.Color().fromHEX(colorVal);
                        break;
                    }
                    case 2: {
                        let colorVal: string = BlockColor.greenVal;
                        lump.color = new cc.Color().fromHEX(colorVal);
                        break;
                    }
                    case 3: {
                        let colorVal: string = BlockColor.pinkVal;
                        lump.color = new cc.Color().fromHEX(colorVal);
                        break;
                    }
                    case 4: {
                        let colorVal: string = BlockColor.redVal;
                        lump.color = new cc.Color().fromHEX(colorVal);
                        break;
                    }
                    case 5: {
                        let colorVal: string = BlockColor.yellowVal;
                        lump.color = new cc.Color().fromHEX(colorVal);
                        break;
                    }
                    default: {
                        break;
                    }
                }
            }
        }
    }

    /**
     * @description: 检测消除
     * @param {number} rowValue 行
     * @param {number} colValue 列
     * @return {*}
     */
    private checkClearUp(rowValue: number, colValue: number): void {
        let itemNode: cc.Node = this.blockBg.getChildByName(rowValue + "_" + colValue);
        let colorValue: imageTypeEumn = itemNode.getComponent(xxmItemBlock).getImageType();
        let pot: number[] = itemNode.getComponent(xxmItemBlock).getDimensions();
        // 行值
        let row: number = pot[0];
        // 列值
        let col: number = pot[1];
        for (let i = row - 1; i <= row + 1; i++) {
            for (let j = col - 1; j <= col + 1; j++) {
                // 忽略越界
                if (i < 0 || i > GameConfig.row - 1 || j < 0 || j > GameConfig.column - 1) {
                    continue;
                }
                // 忽略自己
                if (i == row && j == col) {
                    continue;
                }
                // 忽略斜角
                if (i != row && j != col) {
                    continue;
                }
                // 取到相邻节点(正相邻)
                let blockItem: cc.Node = this.blockBg.getChildByName(i + "_" + j);
                if (!blockItem) {
                    continue;
                }
                let comp: xxmItemBlock = blockItem.getComponent(xxmItemBlock);
                // 已锁定状态 || 颜色不同
                if (comp.getBlockLock() || (colorValue != comp.getImageType())) {
                    continue;
                } else {
                    comp.setBlockLock(true);
                    this.sameBlock.push(blockItem);
                }
                // 相邻相同节点的坐标
                this.checkClearUp(i, j);
                trace('坐标, i:', i, 'j:', j);
            }
        }
    }

    /**
     * @description: 执行消除
     */
    private carrayRemove(next: Function): void {
        for (let i = 0; i < this.sameBlock.length; i++) {
            let child: cc.Node = this.sameBlock[i];
            child.getComponent(xxmItemBlock).clearAnimation();
        }
        this.scheduleOnce(() => {
            next && next();
        }, 0.2);
    }

    /**
     * @description: 计算下落
     */
    private checkDrop(next: Function): void {
        let offset: number = 0;
        for (let i = 0; i < GameConfig.column; i++) {
            for (let j = 0; j < GameConfig.row; j++) {
                let itemNode: cc.Node = this.blockBg.getChildByName(j + "_" + i);
                if (itemNode) {
                    let comp: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
                    let pot: number[] = comp.getDimensions();
                    pot = [j - offset, i];
                    comp.moveDownAnimation(pot);
                } else {
                    offset++;
                }
            }
            offset = 0;
        }
        this.scheduleOnce(() => {
            next && next();
        }, 0.2);
    }

    /**
     * @description: 计算左移
     * @return {*}
     */
    private checkLeftMove(next: Function): void {
        let offset: number = 0;
        for (let i = 0; i < GameConfig.column; i++) {
            // 整列是否有值
            let isHave: boolean = false;
            for (let j = 0; j < GameConfig.row; j++) {
                let itemNode: cc.Node = this.blockBg.getChildByName(j + "_" + i);
                if (itemNode) {
                    isHave = true;
                    continue;
                }
            }

            // 整列没有值
            if (!isHave) {
                offset++;
            }
            if (offset > 0) {
                for (let k = 0; k < GameConfig.row; k++) {
                    let itemNode: cc.Node = this.blockBg.getChildByName(k + "_" + i);
                    if (itemNode) {
                        itemNode.getComponent(xxmItemBlock).setOffset(offset);
                    }
                }
            }
        }
        // 开始向左偏移
        for (let i = 0; i < GameConfig.column; i++) {
            for (let j = 0; j < GameConfig.row; j++) {
                let itemNode: cc.Node = this.blockBg.getChildByName(j + "_" + i);
                if (itemNode) {
                    let comp: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
                    let offset: number = comp.getOffset();
                    let pot: number[] = comp.getDimensions();
                    let pos: number[] = [pot[0], pot[1] - offset];
                    comp.moveLeftAnimation(pos);
                    itemNode.getComponent(xxmItemBlock).setOffset(0);
                }
            }
        }
        this.scheduleOnce(() => {
            next && next();
        }, 0.2);
    }

    /**
     * @description: 点击事件
     * @param {cc} button
     */
    private btnClickCallBack(button: cc.Button) {
        if (button.node.name === "btn_home") {
            GTR.scene.open("StartScene", () => {
                // todo
            })
        } else if (button.node.name === "btnReturn") {
            GTR.ui.close(xxmUIConfig.PlayUI);
            GTR.ui.open(dwfUIConfig.PlayUI);
        } else if (button.node.name === "btnReset") {
            // 重玩
            this.startOver();
        } else if (button.node.name === "colorRemove") {
            // 颜色消除
            this.colorRemove();
        } else if (button.node.name === "changeColor") {
            // 改变颜色
            this.changeColor();
        } else if (button.node.name === "rangeRemove") {
            // 范围消除
            this.rangeRemove();
        }
    }

    /**
     * @description: 重玩
     */
    private startOver(): void {
        GTR.ui.open(xxmUIConfig.ReplayUI);
        LocalData.setItem(LocalCacheDataEnum.SCORE, 0);
    }

    /**
     * @description:  颜色消除:随机消除一种颜色的所有方块
     * @return {*}
     */
    private colorRemove(): void {
        this.sameBlock = [];
        // 颜色随机值
        let randomColor: number = GameUtil.randomIntBound(1, 5);
        for (let i = 0; i < this.blockBg.children.length; i++) {
            let itemNode: cc.Node = this.blockBg.children[i];
            let itemScript: xxmItemBlock = itemNode.getComponent(xxmItemBlock);
            let colorVal: imageTypeEumn = itemScript.getImageType();
            if (randomColor === colorVal) {
                this.sameBlock.push(itemNode);
            }
        }
        // 如果没有随机到颜色值重新随机
        if (!this.sameBlock.length) {
            this.colorRemove();
            return;
        }
        // 执行消除
        this.dealClearUp();
    }

    /**
     * @description: 改变颜色:选择任意一个方块改变成其他的颜色
     * @return {*}
     */
    private changeColor(): void {
        // 改变颜色
        this.usePropBg.active = true;
        // 文案
        let copyWriter: cc.Node = this.usePropBg.getChildByName('copyWriter');
        let copyLabel: cc.Label = copyWriter.getComponent(cc.Label);
        copyLabel.string = '选择星星可以将它改变成想要的颜色';
        this.changeCloor = true;
    }

    /**
     * @description: 获取点击方块的坐标和颜色值
     * @param {object} data
     * @return {*}
     */
    private changeBolckCol(data: { pot, imageType }): void {
        if (this.changeCloor) {
            // 先清除后赋值
            this.clickBlock = [];
            this.imageType = null;
            this.blockLayout.removeAllChildren();
            this.clickBlock = data.pot;
            this.imageType = data.imageType;
            // 设置其他四种颜色值
            let colorValue: number[] = [1, 2, 3, 4, 5];
            for (let i = 0; i < colorValue.length; i++) {
                if (colorValue[i] === this.imageType) {
                    colorValue.splice(i, 1);
                }
            }
            for (let j = 0; j < colorValue.length; j++) {
                // 方块预制
                let itemNode: cc.Node = cc.instantiate(this.blockPrefab);
                itemNode.parent = this.blockLayout;
                // 设置方块颜色
                const item: xxmChangeBlock = itemNode.getComponent(xxmChangeBlock);
                item.setImageType(colorValue[j]);
                item.initData({ colorValue: colorValue[j] });
            }
        }
        if (!this.rangeRemoved) {
            // 行值
            let row: number = data.pot[0];
            // 列值
            let col: number = data.pot[1];
            for (let i = row - 1; i <= row + 1; i++) {
                for (let j = col - 1; j <= col + 1; j++) {
                    // 忽略越界
                    if (i < 0 || i > GameConfig.row - 1 || j < 0 || j > GameConfig.column - 1) {
                        continue;
                    }
                    // 取到相邻节点(正相邻)
                    let blockItem: cc.Node = this.blockBg.getChildByName(i + "_" + j);
                    if (!blockItem) {
                        continue;
                    }
                    this.sameBlock.push(blockItem);
                }
            }
            this.rangeRemoved = true;
            // 执行消除
            this.dealClearUp();
        }
    }

    /**
     * @description: 改变点击方块的颜色
     * @param {object} data
     */
    private changeClickBlock(data: { imageType }): void {
        // 行值
        let row: number = this.clickBlock[0];
        // 列值
        let col: number = this.clickBlock[1];
        // 颜色值
        let colorVal: number = data.imageType;
        let blockItem: cc.Node = this.blockBg.getChildByName(row + "_" + col);
        let itemScript: xxmItemBlock = blockItem.getComponent(xxmItemBlock);
        itemScript.createBlockImg(colorVal);
        itemScript.setImageType(colorVal);
        // 颜色值改变后恢复游戏
        this.usePropBg.active = false;
        this.changeCloor = false;
    }

    /**
     * @description: 范围消除: 消除点击方块周围 3*3 格子
     * @return {*}
     */
    private rangeRemove(): void {
        this.sameBlock = [];
        // 范围消除
        this.usePropBg.active = true;
        this.rangeRemoved = false;
        // 文案
        let copyWriter: cc.Node = this.usePropBg.getChildByName('copyWriter');
        let copyLabel: cc.Label = copyWriter.getComponent(cc.Label);
        copyLabel.string = '点击星星可以将它删除';
        let block: cc.Node = this.usePropBg.getChildByName('block');
        block.active = false;
    }

    /**
     * @description: 当该组件被销毁时调用
     */
    public onDestroy(): void {
        this.positionMap = [];
        this.sameBlock = [];
        this.placeCoordinate = [];
        this.canPlacePos = [];
        this.colorValue = [];
        this.oneBlockCol = [];
        this.residueSameBlock = [];
        this.clickBlock = [];
        this.imageType = null;
        this.changeCloor = false;
        this.rangeRemoved = true;
        this.unscheduleAllCallbacks();
        this.releaseEvents();
    }
}
